﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DatabaseInfoExtended.cs" company="SmartPlan">
//   SmartPlan 2011
// </copyright>
// <summary>
//   Defines the DatabaseInfoExtended type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace DbDiff.Common.Model
{
    using System;
    using System.Data;
    using System.Data.SqlClient;
    using System.Runtime.Remoting.Messaging;
    using Comparer;
    using Helper;

    /// <summary>
    /// The database info extended.
    /// </summary>
    public class DatabaseInfoExtended
    {
        #region constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="DatabaseInfoExtended"/> class.
        /// </summary>
        /// <param name="databaseObjectTypes">
        /// The database object types.
        /// </param>
        /// <param name="databaseInfoBase">
        /// The database info base.
        /// </param>
        public DatabaseInfoExtended(DatabaseObjectTypes databaseObjectTypes, DatabaseInfoBase databaseInfoBase)
        {
            this.DatabaseInfoBase = databaseInfoBase;
            this.SqlVersion = databaseInfoBase.CurrentSqlVersion;
            this.QueryTimeOut = Convert.ToInt32(this.DatabaseInfoBase.SqlObjectOptions.GetCompareOptionValue(OptionNames.QueryTimeOut));
            this.DatabaseObjectTypes = databaseObjectTypes;
        }

        #endregion

        #region delegates

        /// <summary>
        /// The fill data set delegate.
        /// </summary>
        /// <param name="schema">
        /// The schema.
        /// </param>
        /// <param name="table">
        /// The table.
        /// </param>
        private delegate void FillDataSetDelegate(string schema, string table);

        #endregion

        #region properties

        /// <summary>
        /// Gets or sets sql version.
        /// </summary>
        protected SqlVersionInternal SqlVersion { get; set; }

        /// <summary>
        /// Gets or sets the database infobase obj.
        /// </summary>
        protected DatabaseInfoBase DatabaseInfoBase { get; set; }

        /// <summary>
        /// Gets or sets the query time out.
        /// </summary>
        protected int QueryTimeOut { get; set; }

        /// <summary>
        /// Gets or sets the data set name.
        /// </summary>
        protected DatabaseObjectTypes DatabaseObjectTypes { get; set; }

        #endregion

        #region public methods
        
        /// <summary>
        /// The get option bool.
        /// </summary>
        /// <param name="cmpOptions">
        /// The cmp options.
        /// </param>
        /// <param name="optionName">
        /// The option name.
        /// </param>
        /// <returns>
        /// Returns the options value
        /// </returns>
        public static bool GetOptionBool(SqlCompareOptions cmpOptions, string optionName)
        {
            // WARNING OptionNames.isIgnoreOptions == true will ignore all GetOption
            return cmpOptions.GetCompareOptionBoolValue(OptionNames.IsIgnoreOptions) != true && cmpOptions.GetCompareOptionBoolValue(optionName);
        }

        /// <summary>
        /// The get option.
        /// </summary>
        /// <param name="cmpOptions">
        /// The cmp options.
        /// </param>
        /// <param name="optionName">
        /// The option name.
        /// </param>
        /// <returns>
        /// Returns the options value
        /// </returns>
        public static string GetOption(SqlCompareOptions cmpOptions, string optionName)
        {
            // WARNING OptionNames.isIgnoreOptions == true will ignore all GetOption
            return cmpOptions.GetCompareOptionBoolValue(OptionNames.IsIgnoreOptions) ? string.Empty : cmpOptions.GetCompareOptionValue(optionName);
        }

        /// <summary>
        /// Async FillDataset End
        /// </summary>
        /// <param name="result">
        /// The async result
        /// </param>
        public static void EndFillDataSet(IAsyncResult result)
        {
            var d = (FillDataSetDelegate)((AsyncResult)result).AsyncDelegate;
            d.EndInvoke(result);
        }

        /// <summary>
        /// The fill data set.
        /// </summary>
        /// <param name="schema">
        /// The schema.
        /// </param>
        /// <param name="table">
        /// The table.
        /// </param>
        /// <exception cref="ApplicationException">
        /// </exception>
        public virtual void FillDataSet(string schema, string table)
        {
            try
            {
                using (var connection = new SqlConnection(this.DatabaseInfoBase.ConnectionString))
                {
                    using (var dataAdapter = new SqlDataAdapter())
                    {
                        // WARNING OptionNames.isIgnoreOptions == true will ignore all GetOption
                        dataAdapter.SelectCommand = new SqlCommand(this.SqlCommandString().Replace("\t", " "), connection);
                        dataAdapter.SelectCommand.Parameters.Add("@ProcSchema", SqlDbType.NVarChar, 400, "ProcSchema").
                            Value = (GetOption(this.DatabaseInfoBase.SqlObjectOptions, OptionNames.Schema).Trim().Length != 0)
                                        ? schema
                                        : "%";
                        dataAdapter.SelectCommand.Parameters.Add("@Name", SqlDbType.NVarChar, 400, "Name").Value =
                            (GetOption(this.DatabaseInfoBase.SqlObjectOptions, OptionNames.Name).Trim().Length != 0)
                                        ? table
                                        : "%";
                        dataAdapter.SelectCommand.CommandTimeout = this.QueryTimeOut;
                        connection.Open();

                        dataAdapter.MissingMappingAction = MissingMappingAction.Passthrough;
                        dataAdapter.MissingSchemaAction = MissingSchemaAction.Add;
                        dataAdapter.Fill(this.DatabaseInfoBase.DatabaseDataSets.Tables[DatabaseObjectDictionary.ObjectTypes[this.DatabaseObjectTypes]]);
                    }

                    connection.Close();
                }
            }
            catch (Exception e)
            {
                throw new ApplicationException(string.Format("{0} : {1}", e.Message, this.SqlCommandString()), e);
            }
        }

        #region async

        /// <summary>
        /// FillDataset Begin
        /// </summary>
        /// <param name="schema">
        /// The schema name
        /// </param>
        /// <param name="table">
        /// The table name
        /// </param>
        /// <param name="ac">
        /// The async callback
        /// </param>
        /// <param name="state">
        /// The state of the fill
        /// </param>
        /// <returns>
        /// The async result
        /// </returns>
        public IAsyncResult BeginFillDataSet(string schema, string table, AsyncCallback ac, object state)
        {
            var d = new FillDataSetDelegate(this.FillDataSet);
            IAsyncResult result = d.BeginInvoke(schema, table, ac, state);
            return result;
        }

        /// <summary>
        /// Async FillDataset Begin
        /// </summary>
        /// <param name="schema">
        /// The schema name
        /// </param>
        /// <param name="table">
        /// The table name
        /// </param>
        /// <returns>
        /// The async result
        /// </returns>
        public IAsyncResult BeginFillDataSet(string schema, string table)
        {
            var d = new FillDataSetDelegate(this.FillDataSet);
            IAsyncResult result = d.BeginInvoke(schema, table, null, null);
            return result;
        }

        #endregion async

        #endregion

        #region protected methods

        /// <summary>
        /// The sql command string.
        /// </summary>
        /// <returns>
        /// The command string for SqlCommand.
        /// </returns>
        protected string SqlCommandString()
        {
            return this.DatabaseInfoBase.GetSqlCommand(this.DatabaseObjectTypes, this.SqlVersion);
        }
        #endregion
    }
}